<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        //作用域: 代码起作用的区域
        //☞全局作用域: 函数外部都是全局作用域
        定义的变量(全局变量), 在任何的地方访问
        //☞局部作用域: 函数内部就是局部作用域
        在局部作用域中定义的变量(局部变量), 只能在当前作用域中使用
        function fn() {
            let a = 123;
            return a;
        }
        //☞块级作用域: {} let 或者 const关键字
        在块级作用域中定义的变量, 只能在当前作用域中使用
        if (true) {
            let a = 1
        }



        // 作用域链
        // 作用: 在程序中指导变量查找规则
        // 作用域链: 当程序在执行变量的时候,先在当前作用域中查找是否有变量,如果有则执行当前作用域中变量, 如果没有则沿着作用域链向上一级继续查找,找到为止
        // 在嵌套作用域中,多个作用域形成链状结构 ---> 作用域链





        // 函数提升 + 变量提升 
        // 函数提升:
        在程序中遇到函数的时候, 自动将函数的声明提升到当前作用域的开始位置, 不包含函数的调用
        // 函数提升案例
        fn();
        function fn() {
            console.log(123);
        }


        // 变量提升: 返回值为 undefined
        使用var关键字定义的变量, 那么会将变量的声明提升到当前作用域的开始位置, 不包含变量的赋值
        let 没有变量提升

        // 变量提升案例
        结果 ? 报错   原因: let 关键字定义的变量, 必须先定义后使用.let关键字定义的变量, 没有变量提升
        console.log(a);
        let a = 123;

        原因 ? undefined    原因: var 关键字声明的变量有变量提升
        console.log(b);
        var b = 123;





        // 闭包函数: 在函数fn中, 通过另外一个函数fn1使用了当前函数fn中的变量, 整体fn就叫闭包函数
        例: fn1中调用了a, 局部作用域中没有就向上寻找父级元素的a并调用, 此时整个fn都叫闭包函数
        function fn() {
            let a = 123;
            function fn1() {
                return console.log(a + 1);;
            }
            fn1();
        }
        fn();







        // 函数参数默认值
        为防止出现实参与形参数量不同而出现undefined, 给形参设置0的默认值
        function fn(a = 0, b = 0) {
            return a + b;
        }
        fn();





        // arguments    伪数组
        ☞ 在函数中用来保存实参信息的伪数组
        ☞ arguments.length 保存了实参的个数
        ☞ 通过循环遍历 arguments[索引] 得到具体实参的值
        ☞ 当形参个数不确定的时候, 使用arguments获取实参
        // 例:
        function cc() {
            for (let i = 0; i < arguments.length; i++) {
                console.log(arguments[i]);
            }
        }
        cc('aa', 'cc', 'pp')






        // 剩余参数:    ...参数名
        ☞ 在函数中通过  ...参数名
        ☞ 剩余参数最后是以数组形式存在的
        ☞ 剩余参数保存的也是实参的值
        ☞ 剩余参数必须写到形参的最后
        // 例:
        function fn(a, b, ...d) {
            // d 是剩余参数
            console.log(d);
        }
        fn(1, 2, 3, 4)






        // 箭头函数: 
        ☞ (形参1, 形参2) => { 函数体 }
        ☞ 按照自调用函数写法, 调用箭头函数((形参1, 形参2) => { 函数体 })();
        ☞ 将箭头函数赋值给一个变量, 通过  变量名() 调用;

        ☞ 如果只有一个形参, 则() 可以省略              形参1 => { 函数体 }
        ☞ 如果函数体中只有一行代码, 则 { } 可以省略.   (形参1, 形参2) => 函数体
        ☞ 如果函数体中只有一行代码且有返回值, 则 { } 和 return 可以省略
        ☞ 箭头函数中的this, 指向上级作用域中this的事件源
        // 例:
        let btn = document.querySelector('button')
        btn.onclick = function () {
            let anniu = () => {
                console.log(this);
            }
            anniu()
        }




        // let const var
        const关键字: 定义常量的(1, 常量的值不能修改  2, 定义常量必须设置默认值, 3, cosnt会形参块级作用域)
        var关键字: 定义变量的(1. 变量可以先使用后定义  2, 变量提升   3 不会形成块级作用域)

    </script>
</body>

</html>